package butines.core.util;
import java.util.LinkedList;

public class ThreadPool extends ThreadGroup {

	private static int threadPoolNextID;

	private int threadNextID;
	private boolean isAlive;
	private LinkedList<Runnable> taskQueue;
	
	/**
	    Creates a new ThreadPool.
	    @param numThreads The number of threads in the pool.
	*/
	public ThreadPool(int numThreads) {
		super("ThreadPool-" + (threadPoolNextID++));
        setDaemon(true);
		isAlive = true;
        taskQueue = new LinkedList<Runnable>();
        
        for (int i = 0; i < numThreads; i++) {
            new PooledThread().start();
        }
        
	}
	
    /**
	    Requests a new task to run. This method returns immediately, and the task executes on the next available
	    idle thread in this ThreadPool.
	    <p>Tasks start execution in the order they are received.
	    @param task The task to run. If null, no action is taken.
	    @throws IllegalStateException if this ThreadPool is already closed.
	*/
	public synchronized void run(Runnable task) {
	    if (!isAlive) {
	        throw new IllegalStateException();
	    }
	    if (task == null) {
	    	return;
	    }
        taskQueue.add(task);
        notify();
	}

    protected synchronized Runnable getTask() throws InterruptedException {
	    while (taskQueue.size() == 0) {
	        if (!isAlive) {
	            return null;
	        }
	        wait();
	    }
	    return (Runnable)taskQueue.removeFirst();
    }

    /**
	    Closes this ThreadPool and returns immediately. All threads are stopped, and any waiting tasks are not
	    executed. Once a ThreadPool is closed, no more tasks can be run on this ThreadPool.
	*/
	public synchronized void close() {
	    if (isAlive) {
	        isAlive = false;
	        taskQueue.clear();
	        interrupt();
	    }
	}
	
    /**
	 * Closes this ThreadPool and waits for all running threads to finish.
	 * Any waiting tasks are executed.
	*/
	public void join() {
	    // notify all waiting threads that this ThreadPool is no longer alive
	    synchronized (this) {
	        isAlive = false;
	        notifyAll();
	    }
	
	    // wait for all threads to finish
	    Thread[] threads = new Thread[activeCount()];
	    int count = enumerate(threads);
	    for (int i = 0; i < count; i++) {
	        try {
	            threads[i].join();
	        } catch (InterruptedException ex) {
        		System.err.println(ex);
	        }
	    }
	}

	private class PooledThread extends Thread {
		
		public PooledThread() {
			super(ThreadPool.this, "PooledThread-" + (threadNextID++));
		}

		public void run() {
		
	        while (!isInterrupted()) {

	            // get a task to run
	            Runnable task = null;
	            try {
	                task = getTask();
	            } catch (InterruptedException ex) {
	            	System.err.println(ex);
	            }

	            // if getTask() returned null or was interrupted,
	            // close this thread by returning.
	            if (task == null) {
	                break;
	            }

	            // run the task, and eat any exceptions it throws
	            try {
	                task.run();
	            } catch (Throwable t) {
	                uncaughtException(this, t);
	            }
	        }

	    }
		
	}
	
}
